package hxgo

import (
	goLog "log"
	"os"
	"sync"
	"time"
	"path"
	"strings"
	"fmt"
)

// define info log level
const (
	LOG_LEVEL_INFO   = 1
	LOG_LEVEL_WARN   = 10
	LOG_LEVEL_PANIC  = 100
	LOG_FILE_DAILY   = "DAY"
	LOG_FILE_WEEKLY  = "WEEK"
	LOG_FILE_MONTHLY = "MONTH"
)

// define logger interface
type Log interface {
	Init()
	//SetLogger(log *goLog.Logger)
	Info(v...interface {})
	Warn(v...interface {})
	Panic(v...interface {})
}

// LogStd object
type LogStd struct {
	logger *goLog.Logger
	Level int
}

// implement Init func
func (this *LogStd) Init() {
	this.logger = goLog.New(os.Stderr, "", goLog.Ldate | goLog.Ltime)
	this.logger.SetPrefix("")
	this.Level = LOG_LEVEL_INFO
}

// implement SetPrefix func
func (this *LogStd) SetPrefix(prefix string) {
	this.logger.SetPrefix(prefix)
}

// implement SetFlag func
func (this *LogStd) SetFlag(flags int) {
	this.logger.SetFlags(flags)
}

// implement Info func
func (this *LogStd) Info(v...interface {}) {
	if this.Level <= LOG_LEVEL_INFO {
		this.logger.Println(v...)
	}
}

// implement Warn func
func (this *LogStd) Warn(v...interface {}) {
	if this.Level <= LOG_LEVEL_WARN {
		this.logger.Println(v...)
	}
}

// implement Panic func
func (this *LogStd) Panic(v...interface {}) {
	if this.Level <= LOG_LEVEL_PANIC {
		this.logger.Println(v...)
	}
}

// file logger object
type LogFile struct {
	Prefix           string
	Frequency        string
	Level            int
	Path             string
	fsHandler *os.File
	currentFile      string
	locker           sync.Mutex
}

// implement Init func
func (this *LogFile) Init() {
	this.currentFile = this.getCurrentFile()
	this.createFile()
}

// set prefix for file logger
func (this *LogFile) SetPrefix(pre string) {
	this.Prefix = pre;
}

// get current file name by time
func (this *LogFile) getCurrentFile() string {
	fileName := this.Prefix + "_" + time.Now().Format(this.getTimeFormatString())
	fileName += ".log"
	return path.Join(this.Path, fileName)
}

// get time format string by frequency setting
func (this *LogFile) getTimeFormatString() string {
	if this.Frequency == LOG_FILE_DAILY {
		return "2006_01_02"
	}
	if this.Frequency == LOG_FILE_MONTHLY {
		return "2006_01"
	}
	week := time.Now().Day()/7 + 1
	return "2006_01_" + strings.Repeat("W", week)
}

// create file handler
func (this *LogFile) createFile() {
	fs, e := os.OpenFile(this.currentFile, os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0660)
	if e != nil {
		panic(e)
		return
	}
	this.fsHandler = fs
}

// write message to file
func (this *LogFile) writeFile(pre string, v...interface {}) {
	content := time.Now().Format("2006-01-02 15:04:05 [" + pre + "] ") + fmt.Sprintln(v...)
	this.locker.Lock()
	defer this.locker.Unlock()
	this.fsHandler.Write([]byte(content))
}

// check file is proper with time
func (this *LogFile) checkFile() {
	if this.currentFile != this.getCurrentFile() {
		this.locker.Lock()
		if this.fsHandler != nil {
			this.fsHandler.Close()
		}
		this.currentFile = this.getCurrentFile()
		this.createFile()
		this.locker.Unlock()
	}
}

// implement Info func
func (this *LogFile) Info(v...interface {}) {
	if this.Level <= LOG_LEVEL_INFO {
		this.writeFile("INFO", v...)
		this.checkFile()
	}
}

// implement Warn func
func (this *LogFile) Warn(v...interface {}) {
	if this.Level <= LOG_LEVEL_WARN {
		this.writeFile("WARN", v...)
		this.checkFile()
	}
}

// implement Panic func
func (this *LogFile) Panic(v...interface {}) {
	if this.Level <= LOG_LEVEL_PANIC {
		this.writeFile("PANIC", v...)
		this.checkFile()
	}
}

// create new LogFile with basic settings
func NewFileLogger(dir string, prefix string, frequency string, level int) *LogFile {
	logger := &LogFile{}
	logger.Path = dir
	logger.Prefix = prefix
	logger.Frequency = frequency
	logger.Level = level
	logger.Init()
	return logger
}

//--------------

// Logger object, logging something with several loggers by level
type Logger struct {
	loggers map[string]Log
}

// init func
func (this *Logger) Init() {
	this.loggers = make(map[string]Log)
}

// set logger by name string
func (this *Logger) SetLogger(name string, log Log) {
	log.Init()
	this.loggers[name] = log
}

// info func
func (this *Logger) Info(v...interface {}) {
	for _, logger := range this.loggers {
		logger.Info(v...)
	}
}

// warn func
func (this *Logger) Warn(v...interface {}) {
	for _, logger := range this.loggers {
		logger.Warn(v...)
	}
}

// panic func
func (this *Logger) Panic(v...interface {}) {
	for _, logger := range this.loggers {
		logger.Panic(v...)
	}
}

//------------------------

// get new Logger with LogStd as standard one
func NewLogger() *Logger {
	logger := &Logger{}
	logger.Init()
	logger.SetLogger("std", &LogStd{})
	//logger.SetLogger("file", NewFileLogger(dir, prefix, LOG_FILE_DAILY, LOG_LEVEL_INFO))
	return logger
}




